package par

import (
	"testing"

	"gitcode.com/deyiyangyang/bampoo/ast"
	"gitcode.com/deyiyangyang/bampoo/lex"
)

func TestParseStringLiteral(t *testing.T) {
	input := `"hello world"`
	lexer := lex.New(input)
	parser := New(lexer)

	program := parser.ParseProgram()

	if len(parser.Errors()) > 0 {
		t.Fatalf("parser errors: %v", parser.Errors())
	}

	if len(program.Expressions) != 1 {
		t.Fatalf("expected 1 expression, got %d", len(program.Expressions))
	}

	strLit, ok := program.Expressions[0].(*ast.StringLiteral)
	if !ok {
		t.Fatalf("expected StringLiteral, got %T", program.Expressions[0])
	}

	if strLit.Value != "hello world" {
		t.Errorf("expected 'hello world', got '%s'", strLit.Value)
	}
}

func TestParseSymbol(t *testing.T) {
	input := `test-symbol`
	lexer := lex.New(input)
	parser := New(lexer)

	program := parser.ParseProgram()

	if len(parser.Errors()) > 0 {
		t.Fatalf("parser errors: %v", parser.Errors())
	}

	if len(program.Expressions) != 1 {
		t.Fatalf("expected 1 expression, got %d", len(program.Expressions))
	}

	symbol, ok := program.Expressions[0].(*ast.Symbol)
	if !ok {
		t.Fatalf("expected Symbol, got %T", program.Expressions[0])
	}

	if symbol.Value != "test-symbol" {
		t.Errorf("expected 'test-symbol', got '%s'", symbol.Value)
	}
}

func TestParseDefine(t *testing.T) {
	input := `(define s "hello world")`
	lexer := lex.New(input)
	parser := New(lexer)

	program := parser.ParseProgram()

	if len(parser.Errors()) > 0 {
		t.Fatalf("parser errors: %v", parser.Errors())
	}

	if len(program.Expressions) != 1 {
		t.Fatalf("expected 1 expression, got %d", len(program.Expressions))
	}

	define, ok := program.Expressions[0].(*ast.DefineExpr)
	if !ok {
		t.Fatalf("expected DefineExpr, got %T", program.Expressions[0])
	}

	if define.Name.Value != "s" {
		t.Errorf("expected name 's', got '%s'", define.Name.Value)
	}

	value, ok := define.Value.(*ast.StringLiteral)
	if !ok {
		t.Fatalf("expected StringLiteral as value, got %T", define.Value)
	}

	if value.Value != "hello world" {
		t.Errorf("expected value 'hello world', got '%s'", value.Value)
	}
}

func TestParseDisplay(t *testing.T) {
	input := `(display "hello world")`
	lexer := lex.New(input)
	parser := New(lexer)

	program := parser.ParseProgram()

	if len(parser.Errors()) > 0 {
		t.Fatalf("parser errors: %v", parser.Errors())
	}

	if len(program.Expressions) != 1 {
		t.Fatalf("expected 1 expression, got %d", len(program.Expressions))
	}

	display, ok := program.Expressions[0].(*ast.DisplayExpr)
	if !ok {
		t.Fatalf("expected DisplayExpr, got %T", program.Expressions[0])
	}

	value, ok := display.Value.(*ast.StringLiteral)
	if !ok {
		t.Fatalf("expected StringLiteral as value, got %T", display.Value)
	}

	if value.Value != "hello world" {
		t.Errorf("expected value 'hello world', got '%s'", value.Value)
	}
}

func TestParseMultipleExpressions(t *testing.T) {
	input := `(define s "hello") (display s)`
	lexer := lex.New(input)
	parser := New(lexer)

	program := parser.ParseProgram()

	if len(parser.Errors()) > 0 {
		t.Fatalf("parser errors: %v", parser.Errors())
	}

	if len(program.Expressions) != 2 {
		t.Fatalf("expected 2 expressions, got %d", len(program.Expressions))
	}

	// First expression should be define
	define, ok := program.Expressions[0].(*ast.DefineExpr)
	if !ok {
		t.Fatalf("expected DefineExpr as first expression, got %T", program.Expressions[0])
	}

	if define.Name.Value != "s" {
		t.Errorf("expected name 's', got '%s'", define.Name.Value)
	}

	// Second expression should be display
	display, ok := program.Expressions[1].(*ast.DisplayExpr)
	if !ok {
		t.Fatalf("expected DisplayExpr as second expression, got %T", program.Expressions[1])
	}

	symbol, ok := display.Value.(*ast.Symbol)
	if !ok {
		t.Fatalf("expected Symbol as display value, got %T", display.Value)
	}

	if symbol.Value != "s" {
		t.Errorf("expected symbol 's', got '%s'", symbol.Value)
	}
}

func TestParseEmptyList(t *testing.T) {
	input := `()`
	lexer := lex.New(input)
	parser := New(lexer)

	program := parser.ParseProgram()

	if len(parser.Errors()) > 0 {
		t.Fatalf("parser errors: %v", parser.Errors())
	}

	if len(program.Expressions) != 1 {
		t.Fatalf("expected 1 expression, got %d", len(program.Expressions))
	}

	list, ok := program.Expressions[0].(*ast.List)
	if !ok {
		t.Fatalf("expected List, got %T", program.Expressions[0])
	}

	if len(list.Elements) != 0 {
		t.Errorf("expected empty list, got %d elements", len(list.Elements))
	}
}

func TestParseIntegerLiteral(t *testing.T) {
	tests := []struct {
		input    string
		expected int64
	}{
		{"42", 42},
		{"0", 0},
		{"-123", -123},
		{"9999", 9999},
	}

	for _, tt := range tests {
		lexer := lex.New(tt.input)
		parser := New(lexer)

		program := parser.ParseProgram()

		if len(parser.Errors()) > 0 {
			t.Fatalf("parser errors for input %q: %v", tt.input, parser.Errors())
		}

		if len(program.Expressions) != 1 {
			t.Fatalf("expected 1 expression for input %q, got %d", tt.input, len(program.Expressions))
		}

		intLit, ok := program.Expressions[0].(*ast.IntegerLiteral)
		if !ok {
			t.Fatalf("expected IntegerLiteral for input %q, got %T", tt.input, program.Expressions[0])
		}

		if intLit.Value != tt.expected {
			t.Errorf("expected value %d for input %q, got %d", tt.expected, tt.input, intLit.Value)
		}
	}
}

func TestParseArithmeticExpression(t *testing.T) {
	tests := []struct {
		input    string
		operator string
		operands []int64
	}{
		{"(+ 1 2)", "+", []int64{1, 2}},
		{"(- 10 5)", "-", []int64{10, 5}},
		{"(* 3 4 5)", "*", []int64{3, 4, 5}},
		{"(/ 100 10)", "/", []int64{100, 10}},
		{"(+ -1 2 -3)", "+", []int64{-1, 2, -3}},
	}

	for _, tt := range tests {
		lexer := lex.New(tt.input)
		parser := New(lexer)

		program := parser.ParseProgram()

		if len(parser.Errors()) > 0 {
			t.Fatalf("parser errors for input %q: %v", tt.input, parser.Errors())
		}

		if len(program.Expressions) != 1 {
			t.Fatalf("expected 1 expression for input %q, got %d", tt.input, len(program.Expressions))
		}

		arithExpr, ok := program.Expressions[0].(*ast.ArithmeticExpr)
		if !ok {
			t.Fatalf("expected ArithmeticExpr for input %q, got %T", tt.input, program.Expressions[0])
		}

		if arithExpr.Operator != tt.operator {
			t.Errorf("expected operator %q for input %q, got %q", tt.operator, tt.input, arithExpr.Operator)
		}

		if len(arithExpr.Operands) != len(tt.operands) {
			t.Fatalf("expected %d operands for input %q, got %d", len(tt.operands), tt.input, len(arithExpr.Operands))
		}

		for i, expected := range tt.operands {
			intLit, ok := arithExpr.Operands[i].(*ast.IntegerLiteral)
			if !ok {
				t.Fatalf("expected IntegerLiteral operand %d for input %q, got %T", i, tt.input, arithExpr.Operands[i])
			}
			if intLit.Value != expected {
				t.Errorf("expected operand %d to be %d for input %q, got %d", i, expected, tt.input, intLit.Value)
			}
		}
	}
}

func TestParseMixedExpressions(t *testing.T) {
	input := `(define x 42) (display (+ x 8))`
	lexer := lex.New(input)
	parser := New(lexer)

	program := parser.ParseProgram()

	if len(parser.Errors()) > 0 {
		t.Fatalf("parser errors: %v", parser.Errors())
	}

	if len(program.Expressions) != 2 {
		t.Fatalf("expected 2 expressions, got %d", len(program.Expressions))
	}

	// First expression should be define with integer value
	define, ok := program.Expressions[0].(*ast.DefineExpr)
	if !ok {
		t.Fatalf("expected DefineExpr as first expression, got %T", program.Expressions[0])
	}

	if define.Name.Value != "x" {
		t.Errorf("expected name 'x', got '%s'", define.Name.Value)
	}

	intValue, ok := define.Value.(*ast.IntegerLiteral)
	if !ok {
		t.Fatalf("expected IntegerLiteral as define value, got %T", define.Value)
	}

	if intValue.Value != 42 {
		t.Errorf("expected value 42, got %d", intValue.Value)
	}

	// Second expression should be display with arithmetic expression
	display, ok := program.Expressions[1].(*ast.DisplayExpr)
	if !ok {
		t.Fatalf("expected DisplayExpr as second expression, got %T", program.Expressions[1])
	}

	arithExpr, ok := display.Value.(*ast.ArithmeticExpr)
	if !ok {
		t.Fatalf("expected ArithmeticExpr as display value, got %T", display.Value)
	}

	if arithExpr.Operator != "+" {
		t.Errorf("expected operator '+', got '%s'", arithExpr.Operator)
	}

	if len(arithExpr.Operands) != 2 {
		t.Fatalf("expected 2 operands, got %d", len(arithExpr.Operands))
	}

	// First operand should be symbol x
	symbol, ok := arithExpr.Operands[0].(*ast.Symbol)
	if !ok {
		t.Fatalf("expected Symbol as first operand, got %T", arithExpr.Operands[0])
	}
	if symbol.Value != "x" {
		t.Errorf("expected symbol 'x', got '%s'", symbol.Value)
	}

	// Second operand should be integer 8
	intLit, ok := arithExpr.Operands[1].(*ast.IntegerLiteral)
	if !ok {
		t.Fatalf("expected IntegerLiteral as second operand, got %T", arithExpr.Operands[1])
	}
	if intLit.Value != 8 {
		t.Errorf("expected value 8, got %d", intLit.Value)
	}
}
